package org.lisy.java.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 注解使用
 * 
 * @author lisy
 */
public class AnnotationOperate {

	/**
	 * 元注解: (除了元注解，都是自定义注解)
	 * @Target 用于指定被修饰的注解修饰哪些程序单元(类，方法，字段)
	 * @Retention 用于指定被修饰的注解被保留多长时间
	 *  - SOURCE: 注解仅存在于源码中，在 class 字节码文件中不包含
	 *  - CLASS: 默认的保留策略，注解会在 class 字节码文件中存在，但运行时无法获取
	 *  - RUNTIME: 注解会在 class 字节码文件中存在，在运行时可以通过反射获取到
	 * @Documented 用于指定被修饰的注解类将被 javadoc 工具提取成文档
	 * @Inherited 允许子类继承父类中的注解
	 *
	 * 注解的处理机制
	 * 编译时处理: 通过 Annotation Processing Tool (APT) 或 Pluggable Annotation Processing API（Java 6+）处理注解
	 * 运行时处理: 通过 反射（Reflection） 读取注解信息
	 *
	 */

	/**
	 * java 常用注解
	 * @Override 表示当前方法覆盖了父类的方法
	 * @Deprecation 表示方法已经过时,方法上有横线，使用时会有警告
	 * @SuppressWarnings 表示关闭一些警告信息(通知 java 编译器忽略特定的编译警告)
	 * @FunctionalInterface (jdk1.8 更新) 表示：用来指定某个接口必须是函数式接口，否则就会编译出错
	 * 
	 * Spring 常用注解
	 * @Configuration: 把一个类作为一个 IoC 容器，它的某个方法头上如果注册了 @Bean，就会作为这个 Spring 容器中的 Bean
	 * @Lazy(true): 表示延迟初始化
	 * @Service: 用于标注业务层组件
	 * @Controller: 用于标注控制层组件
	 * @Repository: 用于标注数据访问组件，即 DAO 组件
	 * @Component: 泛指组件，当组件不好归类的时候，我们可以使用这个注解进行标注
	 * @Scope: 用于指定 scope 作用域的（用在类上）
	 * @PostConstruct: 用于指定初始化方法（用在方法上）
	 * @PreDestory: 用于指定销毁方法（用在方法上）
	 * @DependsOn: 定义 Bean 初始化及销毁时的顺序
	 * @Primary: 自动装配时当出现多个 Bean 候选者时，被注解为 @Primary 的 Bean 将作为首选者，否则将抛出异常
	 * @Autowired: 默认按类型装配，如果我们想使用按名称装配，可以结合 @Qualifier 注解一起使用
	 * @Resource: 默认按名称装配，当找不到与名称匹配的 bean 才会按类型装配
	 * @PostConstruct: 初始化注解
	 * @PreDestroy: 摧毁注解 默认 单例 启动就加载
	 */
	
	public static void main(String[] args) {
		/**
		 * APT（Annotation Processing Tool): 
		 * 使用 Annotation 修饰了类、方法、Field 等成员时不会自己生效，需要提供处理提取和处理 Annotation 的代码(APT)
		 */
		Person person = new Person("no anno", "no anno", "no anno");
		APTool(person);
		System.out.println(person.toString());
	}
	
	private static void APTool(Object obj) {
		Class<? extends Object> clazz = obj.getClass();
		Field[] declareFields = clazz.getDeclaredFields();
		for (Field field : declareFields) {
			// 检查该类是否使用了某个注解
			if (field.isAnnotationPresent(CustomAnno.class)) {
				CustomAnno anno = field.getAnnotation(CustomAnno.class);
				if (anno != null) {
					String fieldName = field.getName();
					try {
						String methodName = "set" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
						Method setMethod = clazz.getDeclaredMethod(methodName, String.class);
						// 获取注解的属性
						String annoValue = anno.value();
						// 将注解的属性值赋给对应的属性
						setMethod.invoke(obj, annoValue);
					} catch (NoSuchMethodException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					} catch (InvocationTargetException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}

}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Inherited
@interface CustomAnno {
	String value() default "custom anno !";
}

class Person {

	@CustomAnno
	private String stra;
	
	private String strb;
	
	private String strc;

	public Person(String stra, String strb, String strc) {
		super();
		this.stra = stra;
		this.strb = strb;
		this.strc = strc;
	}

	public String getStra() {
		return stra;
	}

	public void setStra(String stra) {
		this.stra = stra;
	}

	public String getStrb() {
		return strb;
	}

	public void setStrb(String strb) {
		this.strb = strb;
	}

	public String getStrc() {
		return strc;
	}

	public void setStrc(String strc) {
		this.strc = strc;
	}

	@Override
	public String toString() {
		return "Person [stra=" + stra + ", strb=" + strb + ", strc=" + strc + "]";
	}

}
